<?php
/**
 *
 *
 * @author Zeon <scplay@gmail.com>
 * @date 2019/2/12 9:47
 * @copyright PanxSoft Inc.
 */

namespace App\Services\Fixer;


use App\Exceptions\ApiErrCode;
use App\Models\Currency;
use App\Models\Setting;
use App\Services\Utils\ApiChecker;
use App\Services\Utils\Transaction;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Database\Eloquent\Collection;

class Fixer
{
    use ApiChecker;

    const END_POINT = 'http://data.fixer.io/api/latest';

    /**
     * @param Schedule $schedule
     */
    public static function schedule(Schedule $schedule)
    {
        $schedule->call(function () {
            if (Setting::getSettingSpecifyValue('config_currency_auto')) {
                (new self)->updateCurrency();
            }
        })->hourly();
    }

    /**
     * 请求 Fixer api 并转换汇率映射表，更新数据库的汇率 value 值
     *
     * @return bool
     *
     * @throws \Exception
     */
    public function updateCurrency()
    {
        $all_currency = Currency::all();
        $default_code = Setting::findConfigByKey('config_currency')->value ?? null;
            $this->checkItemExist($default_code, 'config_currency');
        $default_currency = $all_currency->firstWhere('code', $default_code);
            $this->checkItemExist($default_currency, $default_code);

        $fixer_response = $this->requestFixer();
        $currency_codes = $all_currency
            ->pluck('code')
            ->prepend($fixer_response['base']) // add fixer base currency，因为免费的接口无法设置基准汇率，只能按 fixer 的默认
            ->all();

        $filter_rate_map = array_intersect_key($fixer_response['rates'], array_flip($currency_codes));
        $default_exchange_rate = $filter_rate_map[$default_currency->code] ?? null;
            $this->checkIsTrue($default_exchange_rate, "Default currency code:$default_code not found in Fixer rates");

        $convert_rate_map = array_map(function ($rate) use ($default_exchange_rate) {
            return bcdiv($rate, $default_exchange_rate, 8);
        }, $filter_rate_map);

        $updated = $this->transactionUpdateCurrency($all_currency, $convert_rate_map, $default_currency);

        return $updated;
    }

    private function mockResponseSuccess()
    {
        return $res = [
            "success"   => true,
            "timestamp" => 1549933147,
            "base"      => "EUR",
            "date"      => "2019-02-12",
            "rates"     => [
                "EUR" => 1,
                "AED" => 4.142224,
                "AFN" => 83.956737,
                "USD" => 1.111,
                "ALL" => 124.203777,
                "CNY" => 7.659707,
                // ...
            ],
        ];
    }

    /**
     * @return array|null
     * @throws \App\Exceptions\ApiAbstractException
     */
    private function requestFixer()
    {
        $key = Setting::getSettingSpecifyValue('currency_fixer_access_key');
            $this->checkItemExist($key, 'currency_fixer_access_key');

        $api = self::END_POINT . "?access_key=$key";
        $res = file_get_contents($api);
        /**
         * @see mockResponseSuccess()
         */
        $data = json_decode($res, true);

        $this->checkUnless(json_last_error(), json_last_error_msg());
        $this->checkUnless(empty($data['success']), $res);

        return $data;
    }

    /**
     * @param $all_currency
     * @param array $convert_rate_map
     * @param $default_currency
     *
     * @return bool
     * @throws \Exception
     */
    private function transactionUpdateCurrency(Collection $all_currency,
                                               array $convert_rate_map,
                                               Currency $default_currency)
    {
        $updated = Transaction::handle(function () use ($all_currency, $convert_rate_map, $default_currency) {
            /** @var Currency $currency */
            foreach ($all_currency as $currency) { // 默认货币的汇率转换值是 1 不用更新
                if ($currency !== $default_currency && isset($convert_rate_map[$currency->code])) {
                    $currency->update([
                        'value' => $convert_rate_map[$currency->code],
                    ]);
                }
            }

            return true;
        });

        return $updated;
    }
}